from ast import ASTNode
import lex
import yacc

# Reserved names
reserved = ('restricted',
            'new',
            'tau',
            'true')

# Tokens
tokens = reserved + ('NAME',
                     'NUMBER',
                     'BEGIN',
                     'PARA',
                     'NE',
                     'EQ',
                     'PLUS',
                     'POINT',
                     'COMMA',
                     'LPAR',
                     'RPAR',
                     'LCUR',
                     'RCUR',
                     'LCHEV',
                     'RCHEV',
                     'LSQR',
                     'RSQR')

# Complex token definitions
def t_NAME(t):
    '[a-zA-Z_][a-zA-Z0-9_]*'
    if t.value in reserved:
        t.type = t.value
    return t

def t_NUMBER(t):
    r'\d+'
    t.value = int(t.value)
    return t

def t_error(t):
    print "Illegal character '%s'" % t.value[0]
    t.lexer.skip(1)
    
# TODO: process the C-like comment: /\*(.|\n)*?\*/
# Simple token definitions
t_ignore = ' \t\n'
t_BEGIN = ':!'
t_PARA = r'\|\|'
t_NE = '<>'
t_EQ = '='
t_PLUS = r'\+'
t_POINT = r'\.'
t_COMMA = ','
t_LPAR = '\('
t_RPAR = '\)'
t_LCUR = '{'
t_RCUR = '}'
t_LCHEV = '<'
t_RCHEV = '>'
t_LSQR = r'\['
t_RSQR = r'\]'


start = 'diagram'  # initial symbol, important

def p_diagram(p):
    '''diagram : diagram_declaration diagram_body
               | diagram_body'''
    if len(p) == 3:
        p[0] = ASTNode('', [p[1], p[2]])
    else:
        p[0] = ASTNode('', [p[1]])
    
def p_diagram_declaration(p):
    '''diagram_declaration : restricted restricted_names'''
    p[0] = p[2]
    
def p_restricted_names(p):
    '''restricted_names : NAME
                        | NAME COMMA restricted_names'''
    if len(p) == 2:
        p[0] = ASTNode('', [p[1]])
    else:
        p[0] = ASTNode('', [p[1], p[3]])
    
def p_diagram_body(p):
    '''diagram_body : LSQR replicators_list RSQR'''
    p[0] = p[2]
    
def p_replicators_list(p):
    '''replicators_list : replicator
                        | replicator PARA replicators_list'''
    if len(p) == 2:
        p[0] = ASTNode('', [p[1]])
    else:
        p[0] = ASTNode('', [p[1], p[3]])
    
def p_replicator(p):
    '''replicator : replicator_declaration  replicator_body'''
    p[0] = ASTNode('', [p[1], p[2]])
    
def p_replicator_declaration(p):
    '''replicator_declaration : bound_declaration BEGIN new_names_declaration
                              | bound_declaration BEGIN'''
    if len(p) == 4:
        p[0] = ASTNode('', [p[1], p[3]])
    else:
        p[0] = ASTNode('', [p[1]])
    
def p_bound_declaration(p):
    '''bound_declaration : NAME LSQR NUMBER RSQR'''
    p[0] = ASTNode('bound', [p[1], p[3]])
    
def p_new_names_declaration(p):
    '''new_names_declaration : new LPAR new_names_list RPAR'''
    p[0] = p[3]
    
def p_new_names_list(p):
    '''new_names_list : NAME
                      | NAME COMMA new_names_list'''
    if len(p) == 2:
        p[0] = ASTNode('', [p[1]])
    else:
        p[0] = ASTNode('', [p[1], p[3]])
    
def p_replicator_body(p):
    '''replicator_body : LCUR processes_list RCUR'''
    p[0] = p[2]
    
def p_processes_list(p):
    '''processes_list : process
                      | process PLUS processes_list'''
    if len(p) == 2:
        p[0] = ASTNode('', [p[1]])
    else:
        p[0] = ASTNode('', [p[1], p[3]])
            
def p_process(p):
    '''process : guarded_action POINT LCUR RCUR
               | guarded_action POINT LCUR processes_list RCUR'''
    if len(p) == 5:
        p[0] = ASTNode('', [p[1]])
    else:
        p[0] = ASTNode('', [p[1], p[4]])
    
def p_guarded_action(p):
    '''guarded_action : guard action'''
    p[0] = ASTNode('', [p[1], p[2]])
    
def p_guard(p):
    '''guard : empty
             | name_comparison guard'''
    if len(p) == 2:
        p[0] = ASTNode('', [p[1]])
    else:
        p[0] = ASTNode('', [p[1], p[2]])

def p_empty(p):
    '''empty :'''
    p[0] = ASTNode('empty_guard')
    
def p_name_comparison(p):
    '''name_comparison : match
                       | mismatch'''
    p[0] = p[1]
    
def p_match(p):
    '''match : LSQR NAME EQ NAME RSQR'''
    p[0] = ASTNode('match', [p[2], p[4]])
    
def p_mismatch(p):
    '''mismatch : LSQR NAME NE NAME RSQR'''
    p[0] = ASTNode('mismatch', [p[2], p[4]])
    
def p_action(p):
    '''action : tau_action
              | input_action
              | output_action'''
    p[0] = p[1]
    
def p_tau_action(p):
    '''tau_action : tau'''
    p[0] = ASTNode('tau')

def p_input_action(p):
    '''input_action : NAME LPAR NAME RPAR'''
    p[0] = ASTNode('input', [p[1], p[3]])
    
def p_output_action(p):
    '''output_action : NAME LCHEV NAME RCHEV'''
    p[0] = ASTNode('output', [p[1], p[3]])
    
def p_error(p):
    raise SyntaxError(p)
    
PigLexer = lex.lex()
PigParser = yacc.yacc(method='LALR',
                         debug=1,
                         module=None,
                         tabmodule='pigparsertab',
                         start=None,
                         check_recursion=1,
                         optimize=0,
                         write_tables=1,
                         debugfile='pigparser.out',
                         outputdir='',
                         debuglog=None,
                         errorlog=None,
                         picklefile=None)